/*
 * Copyright 2008 Stuart Moffatt stuartmoffatt@gmail.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.googlecode.emcode.rvha.client.ui;

import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.maps.client.InfoWindow;
import com.google.gwt.maps.client.InfoWindowContent;
import com.google.gwt.maps.client.MapPane;
import com.google.gwt.maps.client.MapPaneType;
import com.google.gwt.maps.client.MapType;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.LargeMapControl;
import com.google.gwt.maps.client.control.MapTypeControl;
import com.google.gwt.maps.client.event.MapClickHandler;
import com.google.gwt.maps.client.event.MapZoomEndHandler;
import com.google.gwt.maps.client.event.MapClickHandler.MapClickEvent;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.LatLngBounds;
import com.google.gwt.maps.client.overlay.GeoXmlLoadCallback;
import com.google.gwt.maps.client.overlay.GeoXmlOverlay;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class MainLayout extends Composite implements WindowResizeListener {

    private static final int DEFAULT_HEIGHT_NORTH = 65;
    private static final int DEFAULT_HEIGHT_SOUTH = 40;
    private static final int DEFAULT_WIDTH_WEST = 100;
    private static final int DEFAULT_WIDTH_EAST = 225;

    protected static final String APPLICATION_TITLE = "Salt Lake County - Rapid Visual Hazard Assessment (BETA)";
    
    protected static final String KML_LIQUEFACTION = "http://hazards.utah.edu/rvha/kmz/eqliq.kmz";
    protected static final String KML_GROUND_MOTION = "http://hazards.utah.edu/rvha/kmz/eqshk.kmz";
    protected static final String KML_LANDSLIDE = "http://hazards.utah.edu/rvha/kmz/lndsl.kmz";
    protected static final String KML_FLOOD = ""; // TODO
    protected static final String KML_AVALANCHE = ""; // TODO
    
    protected static final String KML_TANKS = "http://hazards.utah.edu/rvha/kmz/tanks.kmz";
    protected static final String KML_DAM = "http://hazards.utah.edu/rvha/kmz/damfl.kmz";
    protected static final String KML_TOXIC = "http://hazards.utah.edu/rvha/kmz/txcrl.kmz";
    
    protected static final String KML_SCHOOLS = "http://hazards.utah.edu/rvha/kmz/schls.kmz";
    protected static final String KML_HOSPITALS = "http://hazards.utah.edu/rvha/kmz/hsptl.kmz";
    protected static final String KML_ELDERLY_CARE = "http://hazards.utah.edu/rvha/kmz/eldcr.kmz";

    
    private HashMap overlays = new HashMap();

    private GeoXmlOverlay groundMotionOverlay;
    private GeoXmlOverlay liquefactionOverlay;
    private GeoXmlOverlay landslideOverlay;
    private GeoXmlOverlay floodOverlay;
    private GeoXmlOverlay storageTanksOverlay;
    private GeoXmlOverlay damFailureOverlay;
    private GeoXmlOverlay toxicReleaseOverlay;
    
    private CheckBox currentCheckBox;
    
    // widgets
    private RemoteControl remoteControl = new RemoteControl();
    private Sources sources = new Sources();
    private Help help = new Help();
    private About about = new About();
    
    /**
     * Singleton
     */
    private static MainLayout singleton = null;
    public static MainLayout get()
    {
        if( singleton == null )
        {
            singleton = new MainLayout();
        }
        return singleton;
    }
    
    // members 
    private DockPanel docker = new DockPanel();
    private HorizontalPanel topNav = new HorizontalPanel();;
    private HorizontalPanel south;
    private HorizontalPanel west;
    private MapWidget map;
    private MapPane pane;
    protected Marker marker;
    private VerticalPanel mapSourceContainer = new VerticalPanel();
    private AbsolutePanel groundMotionLegend = new AbsolutePanel();
    private AbsolutePanel liquefactionLegend = new AbsolutePanel();
    private LegendOverlay groundMotionLegendOverlay;
    private LegendOverlay liquefactionLegendOverlay;
    
    // handlers
    HazardInformationHandler hazardInformationHandler = new HazardInformationHandler();
    private Polyline currentPolyline;
    private boolean isGISEnabled = false;
    private HTML helpLink;
    private HTML sourcesLink;
    private VerticalPanel eastVerticalPanel;
    private HTML mapLink;
    private HTML aboutLink;
      
    
    /**
     * Constructor
     *
     */
    public MainLayout () {
        
        initOverlays();
        
        // set up topNav with logos, title
        HTML uLogo = new HTML();
        uLogo.setHTML("<img src='http://hazards.utah.edu/rvha/images/U_Logo_redBck.gif'>");
        
        HTML cnthLogo = new HTML();
        cnthLogo.setHTML("<img src='http://hazards.utah.edu/rvha/images/U_Title_redBck.gif'>");
       
        topNav.add(uLogo);
        topNav.add(cnthLogo);

        Window.setTitle(APPLICATION_TITLE);
        
        HTML title = new HTML(APPLICATION_TITLE);
        title.setStyleName("rvha-Title");
        topNav.add(title);
        topNav.setCellVerticalAlignment(title, HasVerticalAlignment.ALIGN_MIDDLE);
        topNav.setCellHorizontalAlignment(title, HasHorizontalAlignment.ALIGN_RIGHT);
        
        Window.addWindowResizeListener(this);
        
        
        south = new HorizontalPanel();
        
        HTML footer = new HTML("Funded by the Center for Public Policy and Analysis at the University of Utah.");
        footer.setStyleName("rvha-footer");
        south.add(footer);
        
        //remoteControl = new HorizontalPanel();
        //remoteControl.add(new HTML("remoteControl"));
        
        west = new HorizontalPanel();
        west.add(new HTML(""));
        
        map = new MapWidget(new LatLng(40.7105,-111.8491), 10, "pointer", "move");
        initializeMap();
        mapSourceContainer.add(map);
        
        /*
         * Add sources, but hidden for now
         */
        sources.setVisible(false);
        mapSourceContainer.add(sources);
        
        /*
         * Add help, but hidden for now
         */
        help.setVisible(false);
        mapSourceContainer.add(help);
        
        /*
         * Add about, but hidden for now
         */
        about.setVisible(false);
        mapSourceContainer.add(about);
        
        /*
         * Remote Control goes in a vpanel with help and view sources
         */
        helpLink = new HTML();
        helpLink.setHTML("Help");
        helpLink.setStyleName("additional-links");
        helpLink.addClickListener(new ClickListener(){
            public void onClick(Widget arg0) {
               MainLayout.get().showHelp();
            }
        });
        
        sourcesLink = new HTML();
        sourcesLink.setHTML("View data sources");
        sourcesLink.setStyleName("additional-links");
        sourcesLink.addClickListener(new ClickListener(){
            public void onClick(Widget arg0) {
                MainLayout.get().showSources();
            }            
        });
        
        mapLink = new HTML();
        mapLink.setHTML("View map");
        mapLink.setStyleName("additional-links");
        mapLink.addClickListener(new ClickListener(){
            public void onClick(Widget arg0) {
                MainLayout.get().showMap();
            }            
        });
        
        aboutLink = new HTML();
        aboutLink.setHTML("About RVHA");
        aboutLink.setStyleName("additional-links");
        aboutLink.addClickListener(new ClickListener(){
            public void onClick(Widget arg0) {
                MainLayout.get().showAbout();
            }            
        });        
        
        /*
         * Put remote, links(help, sources) in vpanel
         */
        eastVerticalPanel = new VerticalPanel();
        eastVerticalPanel.add(remoteControl);
        eastVerticalPanel.add(helpLink);
        eastVerticalPanel.add(mapLink);        
        eastVerticalPanel.add(sourcesLink);
        eastVerticalPanel.add(aboutLink);
        
        
        docker.add(topNav, DockPanel.NORTH);
        docker.add(south, DockPanel.SOUTH);
        docker.add(eastVerticalPanel, DockPanel.EAST);
        docker.add(west, DockPanel.WEST);
        docker.add(mapSourceContainer, DockPanel.CENTER);
        
        initWidget(docker);

        docker.setCellWidth(eastVerticalPanel, Integer.toString(DEFAULT_WIDTH_EAST));
        docker.setCellHorizontalAlignment(eastVerticalPanel, HasHorizontalAlignment.ALIGN_RIGHT);
        
        onWindowResized(Window.getClientWidth(), Window.getClientHeight());
        
        /*
         * Set up the absolute panels for the layers with legends
         * - groundmotion
         * - liquefaction
         */
        groundMotionLegendOverlay = new LegendOverlay("/rvha/kmz/eqshk.png", 70, 7);
        liquefactionLegendOverlay = new LegendOverlay("/rvha/kmz/eqliq.png", 280, 7);
        
    }

    public void onWindowResized(int width, int height) {
        
        /*
         * 1. Take the window height and width.
         * 2. Subtract the defaults heights and widths of topNav, south, remoteControl, west
         * 3. Derive the height and width of map (be careful of css margins)
         */
        
        // height
        String northHeight = Integer.toString(DEFAULT_HEIGHT_NORTH) + "px";
        String southHeight = Integer.toString(DEFAULT_HEIGHT_SOUTH) + "px";
        
        int middleHeightInt = height - (DEFAULT_HEIGHT_NORTH + DEFAULT_HEIGHT_SOUTH);
        String middleHeight = Integer.toString(middleHeightInt) + "px";
        
        
        docker.setCellHeight(topNav, northHeight);
        docker.setCellHeight(south, southHeight);
        docker.setCellHeight(eastVerticalPanel, middleHeight);
        docker.setCellHeight(west, middleHeight);
        docker.setCellHeight(mapSourceContainer, middleHeight);
        
        // widths
        String windowWidthAsString = Integer.toString(width) + "px";

        
        String eastWidth = Integer.toString(DEFAULT_WIDTH_EAST) + "px";
        String westWidth = Integer.toString(DEFAULT_WIDTH_WEST) + "px";
        
        int centerWidthInt = width - (DEFAULT_WIDTH_EAST + DEFAULT_WIDTH_WEST);
        String centerWidth = Integer.toString(centerWidthInt) + "px";
        
        
        docker.setCellWidth(topNav, windowWidthAsString);
        docker.setCellWidth(south, windowWidthAsString);
        docker.setCellWidth(eastVerticalPanel, eastWidth);
        docker.setCellWidth(west, westWidth);
        docker.setCellWidth(mapSourceContainer, centerWidth);
        
        map.setSize(centerWidth, middleHeight);
        
        sources.resizeFrame(centerWidthInt, middleHeightInt);
        help.resizeFrame(centerWidthInt, middleHeightInt);
        about.resizeFrame(centerWidthInt, middleHeightInt);
    }
    
    public MapWidget getMap()
    {
        return this.map;
    }
    
    private void initializeMap()
    {
        
        // setup marker, but don't add it as an overlay
        marker = new Marker(map.getCenter());
        
        map.addControl(new LargeMapControl());
        map.addControl(new MapTypeControl());
        map.setCurrentMapType(MapType.getNormalMap());
        map.setScrollWheelZoomEnabled(true);
        // 
        
        // add zoom listener
        // map.addMapZoomEndHandler(new GetParcelHandler());
        
        // add click handler
        if( isGISEnabled  )
        {
            map.setDoubleClickZoom(false);
            map.addMapDoubleClickHandler(hazardInformationHandler);
        }
        
        
        // add KML layers
        // loadHazardLayers();
        map.setStyleName("map");
        
    }
    
    public void loadHazardLayers()
    {
        loadLiquefactionOverlay();
        loadDamFailureOverlay();
    }
    
    private void loadDamFailureOverlay() {
        GeoXmlOverlay.load(KML_DAM, new GeoXmlLoadCallback()
        {

          @Override
          public void onFailure(String url, Throwable caught) {
              StringBuffer message = new StringBuffer("KML File " + url
                      + " failed to load");
                  if (caught != null) {
                    message.append(caught.toString());
                  }
                  Window.alert(message.toString());
          }

          @Override
          public void onSuccess(String url, GeoXmlOverlay overlay) {
              map = MainLayout.get().getMap();
              map.addOverlay(overlay);
          }
            
        });
      }
      
      private void loadLiquefactionOverlay() {
        GeoXmlOverlay.load(KML_LIQUEFACTION, new GeoXmlLoadCallback()
        {

          @Override
          public void onFailure(String url, Throwable caught) {
              StringBuffer message = new StringBuffer("KML File " + url
                      + " failed to load");
                  if (caught != null) {
                    message.append(caught.toString());
                  }
                  Window.alert(message.toString());
          }

          @Override
          public void onSuccess(String url, GeoXmlOverlay overlay) {
              map = MainLayout.get().getMap();
              map.addOverlay(overlay);
          }
            
        });
      }

    public void updateMarker(LatLng point, String infoWindowText) {
        
        try
        {
            // remove the old marker first
            if (marker != null)
            {
                map.removeOverlay(marker);
            }          
            
            marker = new Marker(point);
            
            // add an infowindow
            InfoWindow infoWindow = map.getInfoWindow();
            InfoWindowContent content = new InfoWindowContent(infoWindowText);
            
            map.addOverlay(marker);
            
            infoWindow.open(marker, content);
        }
        catch(Exception e)
        {
            GWT.log("updateMarker:" + e.getMessage(), null);
        }
    }

    public void updateMarker(LatLng point) {
        
        // remove the old marker first
        if (marker != null)
        {
            map.removeOverlay(marker);
        }          
        
        marker = new Marker(point);
        map.addOverlay(marker);
        
    }

    public void addParcelGeometry(Polyline polyline) {
        if( currentPolyline != null )
        {
            map.removeOverlay(currentPolyline);
        }
        currentPolyline = polyline;
        map.addOverlay(currentPolyline);
    }    
   
    protected void toggleKml(String url, CheckBox cb) {
        
        GWT.log("toggleKml="+url+",flag="+cb.isChecked(), null);
        GeoXmlOverlay currentOverlay = lookupOverlay(url);
        GWT.log("currentOverlay="+currentOverlay, null);
        
        /*
         * Special check for toggling legends
         */
        
        // handle liquefaction legend
        if( url.equalsIgnoreCase(KML_LIQUEFACTION))
        {
            if(cb.isChecked())
            {
                GWT.log("Turn on liquefaction legend", null);
                toggleLegend(KML_LIQUEFACTION, true);
            }
            else
            {
                GWT.log("Turn off liquefaction legend", null);
               toggleLegend(KML_LIQUEFACTION, false);
            }
        }
        
        // handle ground motion
        if( url.equalsIgnoreCase(KML_GROUND_MOTION))
        {
            if(cb.isChecked())
            {
                GWT.log("Turn on ground motion legend", null);
                toggleLegend(KML_GROUND_MOTION, true);
            }
            else
            {
                GWT.log("Turn off ground motion legend", null);
                toggleLegend(KML_GROUND_MOTION, false);
            }
        } 
        
        // if null, load it (first time loading)
        if( currentOverlay == null )
        {
            
            loadKml(url, cb);
            return;
        }
        
       
        
        // otherwise, set the visibility
        if( url.equalsIgnoreCase(KML_LIQUEFACTION))
        {
            // this is a workaround for the liquefaction layer,
            // which is optimized on Google's servers as tiles.
            // when we zoom in with the layer invisible, the visibility
            // is set. 
            // see http://code.google.com/p/gwt-google-apis/issues/detail?id=107
            // the workaround is to remove it completely from the map
            if(cb.isChecked() )
            {
                map.addOverlay(currentOverlay);
            }
            else
            {
                map.removeOverlay(currentOverlay);
            }
            
        } else {
            // currentOverlay.setVisible(cb.isChecked());
            // instead of setVisible, just add and remove
            if( cb.isChecked() )
            {
                map.addOverlay(currentOverlay);
            }
            else
            {
                map.removeOverlay(currentOverlay);
            }
        }
        
        
    }
    
    private void toggleLegend(String kml, boolean visibility) {
        
        if(kml.equalsIgnoreCase(KML_LIQUEFACTION))
        {
            if(visibility)
            {
                // turn ON the overlay for liquefaction
                map.addOverlay(liquefactionLegendOverlay);
            }
            else
            {
                // turn OFF the overlay for liquefaction
                map.removeOverlay(liquefactionLegendOverlay);
            }
        }
        
        if(kml.equalsIgnoreCase(KML_GROUND_MOTION))
        {
            if(visibility)
            {
                // turn ON the overlay for ground motion
                map.addOverlay(groundMotionLegendOverlay);
            }
            else
            {
                // turn OFF the overlay for ground motion
                map.removeOverlay(groundMotionLegendOverlay);
            }
        }        
        
    }

    private GeoXmlOverlay lookupOverlay(String url)
    {
        return (GeoXmlOverlay) overlays.get(url);
    }  
    
    private void initOverlays()
    {
        overlays.put(KML_DAM, damFailureOverlay);
        overlays.put(KML_FLOOD, floodOverlay);
        overlays.put(KML_LANDSLIDE, landslideOverlay);
        overlays.put(KML_LIQUEFACTION, liquefactionOverlay);
        overlays.put(KML_GROUND_MOTION, groundMotionOverlay);
        overlays.put(KML_TANKS, storageTanksOverlay);
        overlays.put(KML_TOXIC, toxicReleaseOverlay);
    }    
    
    protected void loadKml(String url, CheckBox cb)
    {
        currentCheckBox = cb;
        currentCheckBox.setEnabled(false);
        GeoXmlOverlay.load(url,
                new GeoXmlLoadCallback() {
                  public void onFailure(String url, Throwable e) {
                      kmlLoadFailure(url, e);
                  }
                  public void onSuccess(String url, GeoXmlOverlay overlay) {
                    
                    GWT.log("put overlay into hash="+overlay, null);
                    
                    overlays.put(url, overlay);
                    
                    GeoXmlOverlay currentOverlay = lookupOverlay(url);
                    
                    GWT.log("fetch overlay from hash="+currentOverlay, null);
                    GWT.log("add overlay to map", null);
                    
                    map.addOverlay(currentOverlay);
                    GWT.log("KML File " + url + " loaded successfully", null);
                    GWT.log("Default Center=" + overlay.getDefaultCenter(), null);
                    GWT.log("Default Span=" + overlay.getDefaultSpan(), null);
                    GWT.log("Default Bounds=" + overlay.getDefaultBounds(), null);
                    GWT.log("Supports hide=" + overlay.supportsHide(), null);
                    enableCurrentCheckBox();
                  }
                });         
    }  
    
    protected void kmlLoadFailure(String url, Throwable e)
    {
        StringBuffer message = new StringBuffer("KML File " + url
                + " failed to load");
            if (e != null) {
              message.append(e.toString());
            }
            Window.alert(message.toString());       
    }
    
    protected void enableCurrentCheckBox()
    {
        currentCheckBox.setEnabled(true);
    }

    public void clearMarker() {
        map.removeOverlay(marker);  
    }

    public void showAbout() {
        about.setVisible(true);
        help.setVisible(false);
        sources.setVisible(false);
        map.setVisible(false);
    }
    
    public void showHelp()
    {
        about.setVisible(false);
        help.setVisible(true);
        sources.setVisible(false);
        map.setVisible(false);    
    }
    
    public void showMap() {
        about.setVisible(false);
        help.setVisible(false);
        sources.setVisible(false);
        map.setVisible(true);
    }
    
    public void showSources() {
        about.setVisible(false);
        help.setVisible(false);
        sources.setVisible(true);
        map.setVisible(false);
    }    
}
